#NetworkCommunicator inherits from GServer which is a multithreaded TCP server
require 'gserver'
require 'Connection'
require 'socket'
require 'Message'
require 'thread'
class NetworkCommunicator < GServer
  #@tcp_connections < all the connections on this server
  #@name < the prefered name of the peer TODO: change to an array of names that can be chosen
  #@message_received_listeners < the lambdas that are called when a message is received
  attr_accessor :tcp_onnections, :message_received_listeners, :address, :name
  #does a super class supply something that can be used instead?
  #this is only used for the purposes of checking work port the server is running on.
  attr_reader :port
  #constant value that is used for the default port value
  DEFAULT_PORT = 2305
  #Initialises the parent class and initialises the instance variable @tcp_connections to an new array
  def initialize(port=DEFAULT_PORT, *args)
    @message_received_listeners = Array.new
    @tcp_connections = Array.new
    super(port, *args)
    @port = port
  end
  #Serves out and receives information from clients
  def serve(io)
    #Creates a new connection which will contain information that is specific to this connection
    con = Connection.new(@name)
    #Adds the IP address information to the connection
    con.ip = io.peeraddr[3]
    #Adds the client port information to the connection
    con.client_port = io.peeraddr[1].to_s.chomp
    #loops through andsends out messages and recieves messages
    handle_connections(io, con)
  end
  def handle_connections(io, con)
    begin
      until con.disconnect do
        if IO.select([io],nil,nil,0.1)
          #The line of data reveived
          begin
            line = io.gets.chomp
          rescue=>e
            disconnected(con)
            return
          end
          #Splits the line into an array of strings delimited by "#". This allows for the
          #line to be considered in sections
          #Checks tp see if the first section to the string said was some kind of directive
          Message.new(line,con)
        end
          #send out messages in the message queue
        Message.each_queued_message do |m|
          if m.next_recipeint == con.name
            io.puts(m.message_string)
          end
        end
        Message.message_queue.delete_if {|m| m.next_recipeint == con.name }
        Message.each_received_message do |m|
          message_received(m)
        end
        Message.received_messages.clear
      end
    rescue => e
      puts "Error from main loop in NetworkCommunicator:#{e}"
    end
  end
  #calls each Proc that is required to be notified when a message is received
  def message_received(mes)
    for l in @message_received_listeners
      l.call(mes)
    end
  end
  def disconnected(con)
    #remove the connection when it disconnects
    puts "Disconnecting"
    Connection.connections.delete(con)
  end
  def disconnecting(c_port)
    for c in Connection.connections
      disconnected(c) if c.client_port == c_port
    end
  end
  def connect(host, port, auto_register = true)
    con = Connection.new("")
    #Creates a new thread and adds a connection managment method to the thread.
    #The reason that the method has to be run on a different thread is because of the infinate loop in handle_connections
    #There may be a need for a mutex lock here or in handle_connections
    Thread.new do
      TCPSocket.open(host,port) do |server|
        con.ip = server.peeraddr[3]
        con.client_port = server.peeraddr[1].to_s.chomp
        server.puts("[GOURAMI]{SET NAME:#{@name}}") if auto_register #TODO: this should really be dealt with at a higher level
        handle_connections(server,con)
      end
      puts "Connection failed"
      Connection.connections.delete(con)
    end
  end
end


